using Cysharp.Threading.Tasks;
using Net.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using System.Text;
using Net.Unity;
using Newtonsoft_X.Json;
#if HYBRIDCLR
using HybridCLR;
#endif

namespace GameCore
{
    public class AssetUpdateManager : MonoBehaviour
    {
        public string Url => Global.Config.Url;
        public string MetadataList => Global.Config.MetadataList;
        public string HotfixDll => Global.Config.HotfixDll;
        public bool CheckFileMD5 => Global.Config.CheckFileMD5;
        public bool UseFirstPackage => Global.Config.UseFirstPackage;
        public bool FirstPackageInStreamingAssets => Global.Config.FirstPackageInStreamingAssets;
        public int CheckAssetsRefresh => Global.Config.CheckAssetsRefresh;

        public virtual async void Start()
        {
            var mode = Global.I.Mode;
            if ((byte)mode <= 1)
            {
                _ = LoadAssetBundle();
            }
            else if (mode == AssetBundleMode.LocalMode)
            {
                var versionUrl = Global.Config.GetPlatformPath("version.json");
                var serverPackageDict = await VersionCheck(versionUrl, false);
                if (serverPackageDict == null)
                {
                    Global.Logger.LogError("请先构建AB包再运行! 构建的AB包时要勾选CopyToStreamingAssets");
                    return;
                }
                Global.Resources.PackageManager.ServerPackageDict = serverPackageDict;
                _ = LoadAssetBundle();
            }
            else
            {
                _ = CheckPackageUpdate();
            }
        }

        public async UniTask LoadAssetBundle()
        {
            await Global.Resources.Init();
            await Global.Table.Init();
            await LoadMetadataForAOTAssemblies();
            Global.I.OnInit();
        }

        private async UniTask<Dictionary<string, AssetPackageInfo>> VersionCheck(string url, bool showMsg)
        {
            return await UnityWebRequestEx.GetAsync(url, request =>
            {
                var jsonBytes = request.downloadHandler.data;
                var text = Encoding.UTF8.GetString(jsonBytes);
                var assetBundleInfos = JsonConvert.DeserializeObject<List<AssetPackageInfo>>(text);
                var assetBundleDict = new Dictionary<string, AssetPackageInfo>();
                foreach (var item in assetBundleInfos)
                    assetBundleDict.Add(item.Name, item);
                return assetBundleDict;
            },
            async error =>
            {
                return !showMsg ? (false, default) : (await Global.UI.Message.ShowUIAsync("资源请求", "版本信息请求失败,是否重试?"), default);
            },
            Global.Config.Timeout);
        }

        private async UniTaskVoid CheckPackageUpdate()
        {
            var versionUrl = Global.Config.GetPlatformUrl("version.json");
            var serverPackageDict = await VersionCheck(versionUrl, true);
            if (serverPackageDict == null)
            {
#if UNITY_EDITOR
                UnityEditor.EditorApplication.isPlaying = false;
#endif
                Application.Quit();
                return;
            }
            versionUrl = Global.Config.GetPlatformPath("version.json");
            var localPackageDict = await VersionCheck(versionUrl, false);
            if (localPackageDict == null)
            {
                if (UseFirstPackage)
                {
                    var complete = await FirstPackageDownload(serverPackageDict.Values.ToArray());
                    if (!complete)
                        return;
                    localPackageDict = serverPackageDict; //下载完成后肯定是最新的了, 所以让服务器包=本地包
                }
                else localPackageDict = new Dictionary<string, AssetPackageInfo>();
            }
            Global.Resources.PackageManager.LocalPackageDict = localPackageDict;
            Global.Resources.PackageManager.ServerPackageDict = serverPackageDict;
            var result = await CheckAssetBundle();
            if (!result)
                return;
            Global.Resources.PackageManager.WriteVersionJsonFile(versionUrl, serverPackageDict.Values.ToList());
            await UniTask.Delay(1000);
            _ = LoadAssetBundle();
        }

        protected virtual async UniTask<bool> FirstPackageDownload(AssetPackageInfo[] assetPackageInfos)
        {
            Global.UI.Loading.ShowUI("正在检查资源中...", 0f);
            var downloadBytes = 0L;
            foreach (var assetPackageInfo in assetPackageInfos)
            {
                var localPackageZip = Global.Config.GetVersionPath($"{assetPackageInfo.Name}.zip");
                if (FirstPackageInStreamingAssets || !assetPackageInfo.Needing)
                    continue;
                if (File.Exists(localPackageZip))
                {
                    var md5 = EncryptHelper.ToMD5(localPackageZip);
                    if (assetPackageInfo.MD5 == md5)
                        continue;
                }
                downloadBytes += assetPackageInfo.FileSize;
            }
            if (downloadBytes > 0L)
            {
                var msgResult = await DownloadManager.DownloadRequestTips("资源请求", $"有资源需要更新:{ByteHelper.ToString(downloadBytes)}");
                if (!msgResult)
                    return false;
            }
            var currSize = 0L;
            foreach (var assetPackageInfo in assetPackageInfos)
            {
                var assetPackage = await Global.Resources.PackageManager.GetServerPackageInfo(assetPackageInfo);
                if (assetPackage == null)
                {
                    Global.UI.Message.ShowUI("资源请求", "资源包请求失败!");
                    return false;
                }
                Global.Resources.PackageManager.WriteJsonFile(assetPackage);
                var serverPackageZip = Global.Config.GetVersionUrl($"{assetPackageInfo.Name}.zip");
                var localPackageZip = Global.Config.GetVersionPath($"{assetPackageInfo.Name}.zip");
                if (FirstPackageInStreamingAssets)
                {
                    var item1 = await CopyToPersistentPath($"{Application.streamingAssetsPath}/{assetPackageInfo.Name}.zip", localPackageZip);
                    if (!item1.Item1)
                    {
                        Global.UI.Message.ShowUI("首包复制", $"复制首包文件出错:{item1.Item2}");
                        return false;
                    }
                }
                else if (assetPackageInfo.Needing) //只有必须包才需要下载
                {
                    if (File.Exists(localPackageZip))
                    {
                        var md5 = EncryptHelper.ToMD5(localPackageZip);
                        if (assetPackageInfo.MD5 == md5)
                            continue;
                    }
                    var isComplete = await DownloadManager.DownloadFile(serverPackageZip, localPackageZip, $"资源包{assetPackageInfo.Name}", currSize, downloadBytes);
                    if (!isComplete)
                    {
                        Global.UI.Message.ShowUI("资源请求", "资源包请求失败!");
                        return false;
                    }
                    currSize += assetPackageInfo.FileSize;
                }
            }
            Global.UI.Loading.ShowUI("正在解压资源包...", 0f);
            currSize = 0L;
            downloadBytes = 0L;
            foreach (var assetPackageInfo in assetPackageInfos)
            {
                var localPackageZip = Global.Config.GetVersionPath($"{assetPackageInfo.Name}.zip");
                if (!File.Exists(localPackageZip))
                    continue;
                downloadBytes += assetPackageInfo.FileSize;
            }
            foreach (var assetPackageInfo in assetPackageInfos)
            {
                var localPackageZip = Global.Config.GetVersionPath($"{assetPackageInfo.Name}.zip");
                if (File.Exists(localPackageZip))
                {
                    var result = await UnZipHelper.DecompressFile(localPackageZip, Global.Config.AssetBundlePath, null, (name, progress) =>
                    {
                        var value = assetPackageInfo.FileSize * progress;
                        progress = (currSize + value) / downloadBytes;
                        Global.UI.Loading.ShowUI($"正在解压资源包:{name}", progress);
                    });
                    if (!result)
                    {
                        Global.UI.Loading.ShowUI("解压初始包失败!", 1f);
                        File.Delete(localPackageZip);
                        return false;
                    }
                    File.Delete(localPackageZip);
                    currSize += assetPackageInfo.FileSize;
                }
            }
            Global.UI.Loading.ShowUI("解压完成", 1f);
            await UniTask.Delay(100);
            Global.UI.Loading.HideUI();
            return true;
        }

        private async UniTask<ValueTuple<bool, string>> CopyToPersistentPath(string fileUrl, string destPath)
        {
            return await UnityWebRequestEx.GetAsync(fileUrl, new FileDHParameter(destPath), (oper) => { }, request => (true, string.Empty),
            async error => (await UniTask.FromResult(false), (false, string.Empty)), Global.Config.Timeout);
        }

        private async UniTask<bool> CheckAssetBundle()
        {
            var totalSize = 0L;
            var currSize = 0L;
            var downloadAssetBundleInfos = new List<AssetBundleUpdateInfo>();
            var localPackage = Global.Resources.PackageManager.LocalPackageDict;
            var serverPackage = Global.Resources.PackageManager.ServerPackageDict;
            Global.UI.Loading.ShowUI("正在检查资源中...", 0f);
            foreach (var assetPackageInfo in serverPackage.Values)
            {
                if (localPackage.TryGetValue(assetPackageInfo.Name, out var assetPackageInfo1))
                    if (assetPackageInfo1.MD5 == assetPackageInfo.MD5)
                        continue;
                var assetBundleUpdateInfo = await Global.Resources.PackageManager.GetPackageUpdateInfo(assetPackageInfo.Name);
                if (assetBundleUpdateInfo == null)
                    return false;
                if (!assetPackageInfo.Needing)
                    continue;
                downloadAssetBundleInfos.Add(assetBundleUpdateInfo);
                totalSize += assetBundleUpdateInfo.fileSize;
            }
            if (totalSize > 0)
            {
                var msgResult = await DownloadManager.DownloadRequestTips("资源请求", $"有资源需要更新:{ByteHelper.ToString(totalSize)}");
                if (!msgResult)
                    return false;
            }
            foreach (var downloadAssetBundle in downloadAssetBundleInfos)
            {
                foreach (var assetBundleInfo in downloadAssetBundle.assetBundleUpdateInfos)
                {
                    var serverAssetBundleUrl = Global.Config.GetAssetBundleUrl(assetBundleInfo);
                    var localAssetBundleUrl = Global.Config.GetAssetBundlePath(assetBundleInfo);
                    var isComplete = await DownloadManager.DownloadFile(serverAssetBundleUrl, localAssetBundleUrl, assetBundleInfo.Name, currSize, totalSize);
                    if (!isComplete)
                        return false;
                    currSize += assetBundleInfo.FileSize;
                }
            }
            Global.UI.Loading.HideUI();
            return true;
        }

        /// <summary>
        /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
        /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
        /// </summary>
        private async UniTask LoadMetadataForAOTAssemblies()
        {
#if HYBRIDCLR
            if (Global.HotfixAssembly != null)
                return;
            var textAsset = await Global.Resources.LoadAssetAsync<TextAsset>(MetadataList);
            if (textAsset == null)
            {
                Debug.LogError("获取元数据清单资源失败!");
                return;
            }
            var files = JsonConvert.DeserializeObject<List<string>>(textAsset.text);
            /// 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
            /// 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
            var mode = HomologousImageMode.SuperSet;
            foreach (var dllPath in files)
            {
                var metaAsset = await Global.Resources.LoadAssetAsync<TextAsset>(dllPath);
                if (metaAsset == null)
                {
                    Debug.LogError($"补充元数据dll:{dllPath}获取失败!");
                    continue;
                }
                var dllBytes = metaAsset.bytes;
                // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
                var err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
                Debug.Log($"LoadMetadataForAOTAssembly:{dllPath}. mode:{mode} ret:{err}");
            }
#if UNITY_EDITOR
            //检查热更新是不是在项目里面被加载了， 如果已经加载就不进行Load了
            Global.HotfixAssembly = AssemblyHelper.GetRunAssembly(Path.GetFileNameWithoutExtension(HotfixDll).Replace(".dll", ""));
            if (Global.HotfixAssembly != null)
                return;
#endif
            byte[] rawBytes = null;
            byte[] pdbBytes = null;
            var dllAsset = await Global.Resources.LoadAssetAsync<TextAsset>(HotfixDll);
            if (dllAsset != null)
                rawBytes = dllAsset.bytes;
            var hotfixPdb = HotfixDll.Replace("dll", "pdb");
            var pdbAsset = await Global.Resources.LoadAssetAsync<TextAsset>(hotfixPdb);
            if (pdbAsset != null)
                pdbBytes = pdbAsset.bytes;
            Global.HotfixAssembly = System.Reflection.Assembly.Load(rawBytes, pdbBytes);
            AssemblyHelper.OnAssemblyLoad(this, new AssemblyLoadEventArgs(Global.HotfixAssembly));
#endif
        }
    }
}